package com.cj.common.orm.hibernate;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.annotation.Transactional;

import com.cj.common.entity.IdEntity;
import com.cj.common.orm.Page;
import com.cj.common.orm.PropertyFilter;
import com.cj.common.utils.ReflectionUtils;
import com.cj.common.utils.SelectOptUtil;
import com.cj.common.vo.SelectOpt;
import com.cj.sys.service.ServiceException;

/**
 * 领域对象业务管理类基类.
 * 
 * @param <T>
 *            领域对象类型
 * @param <PK>
 *            领域对象的主键类型
 * 
 *            eg. public class UserManager extends EntityManager<User, Long>{ }
 * 
 * @author calvin
 */
@Transactional
public abstract class EntityManager<T extends IdEntity, PK extends Serializable> {
	
	protected Logger logger = LoggerFactory.getLogger(getClass());

	protected abstract HibernateDao<T, PK> getEntityDao(); 

	// CRUD函数 //

	@Transactional(readOnly = true)
	public T get(final PK id) {
		return getEntityDao().get(id);
	}

	/**
	 * 【load有缓存】
	 */
	@Transactional(readOnly = true)
	public T load(final PK id) {
		return getEntityDao().load(id);
	}

	@Transactional(readOnly = true)
	public Page<T> getAll(final Page<T> page) {
		return getEntityDao().getAll(page);
	}

	@Transactional(readOnly = true)
	public List<T> getAll() {
		return getEntityDao().getAll();
	}

	@Transactional(readOnly = true)
	public Page<T> search(final Page<T> page, final List<PropertyFilter> filters) {
		return getEntityDao().find(page, filters);
	}

	@Transactional(readOnly = true)
	public List<T> search(final List<PropertyFilter> filters){
		return getEntityDao().find(filters);
	}
	public void save(final T entity) throws ServiceException {
		if(!beforeChange(entity)){
			return;
		}
		getEntityDao().save(entity);
		afterChange(entity);
	}

	public void delete(final PK id) throws ServiceException {
		if(!beforeChange(id)){
			return;
		}
		getEntityDao().delete(id);
		afterChange(id);
	}
	
	/**
	 * <p>【新增，修改，删除前执行的方法】</p>
	 *
	 * @param entity
	 * @return
	 */
	protected boolean beforeChange(T entity) {
		return true;
	}
	/**
	 * <p>【新增，修改，删除前执行的方法】</p>
	 *
	 * @param entity
	 * @return
	 */
	protected boolean beforeChange(PK id) {
		return beforeChange(this.load(id));
	}
	/**
	 * <p>【新增，修改，删除后执行的方法】</p>
	 *
	 * @param entity
	 * @return
	 */
	protected void afterChange(T entity) {
	}
	/**
	 * <p>【新增，修改，删除后执行的方法】</p>
	 *
	 * @param entity
	 * @return
	 */
	protected void afterChange(PK id) {
		afterChange(this.load(id));
	}
	
	
	public Page<T> find(final Page<T> page, final String hql,
			final Object... values) {
		return getEntityDao().find(page, hql, values);
	}
	
	
	
	
	//下面为很差实现，不建议使用
	/**
	 * @param menuId
	 * @param action
	 * @param hql
	 * @param obj
	 *            父节点id必须放第一位
	 */
    @Deprecated
	@Transactional(readOnly = true)
	public String getTreeXml(String textProperty,
			String parentProperty, String action,String src,String hql,
			final Object... obj)
			throws Exception {
		try {
			Document doc = DocumentHelper.createDocument();
			doc.setXMLEncoding("UTF-8");
			Element root = doc.addElement("TreeNode");
			getXMLElement(root, textProperty, parentProperty,
					action, src,hql,
					obj);
			return doc.asXML();
		} catch (Exception e) {
			logger.error("生成xml文档数据失败: ", e);
			throw e;
		}
	}
	
	private void getXMLElement(Element root,
			String textPropertyName, String parentProperty, String action,
			String src,String hql, final Object... obj) {
		List<T> nodes;
			nodes = getEntityDao().find(hql, obj);
			
		if (nodes != null) {
			for (T node : nodes) {
				// 下级数据
				String text,parent;
				Object textObj = ReflectionUtils.getFieldValue(node,textPropertyName);
				Object parentObj = ReflectionUtils.getFieldValue(node,parentProperty);
				if(textObj !=null ) {
					text = textObj.toString();
				} else {
					text = "";
				}
				if(parentObj != null) {
					parent = parentObj.toString();
				} else {
					parent = "";
				}
				if (parent.equals(obj[0].toString())) { // 自己为自己父节点去掉
					continue;
				}
				obj[0] = ReflectionUtils.getFieldValue(node, parentProperty);
				List<T> children = getEntityDao().find(hql, obj);					
				// 如果下级还有数据，则将此节点的src设置为有值、点击后再展开，否则无值、点击后不展开
				if (children == null || children.size() == 0) {
					// 没有下级数据
					Element element = root.addElement("TreeNode").addAttribute(
							"text", text)
							.addAttribute(
							"value",
							ReflectionUtils.getFieldValue(node, parentProperty)
									.toString()
									+ ";" + text)
							.addAttribute("checked",
							"false"); // 开始是否被选择
					if (action != null) {
						element.addAttribute("action", action + parent);
					}else{
						element.addAttribute("action",
								"javascript:try{showDetail('" + parent + "','" + text
										+ "');}catch(err){};");
					}
				} else {
					// 有下级数据,需要对src赋值
					Element element = root.addElement("TreeNode").addAttribute(
							"text", text)
					.addAttribute("value",
							parent + ";" + text)
							.addAttribute("src", src + parent).addAttribute("checked",
									"false"); // 开始是否被选择
					if (action != null) {
						element.addAttribute("action", action + parent);
					} else {
						element.addAttribute("action", "javascript:try{showDetail('" + parent
								+ "');}catch(err){};");
					}
				}
			}
		} else {
			root.addElement("TreeNode").addAttribute("text", "无").addAttribute(
					"action", "").addAttribute("checked", "false");
		}
	}

	/**
	 * @param textProperty
	 * @param valueProperty
	 * @param parentProperty
	 * @param hql
	 * @param values
	 *            父节点id必须放第一位
	 * @return
	 * @author:cj [2009-6-13 下午03:02:11]
	 */
    @Deprecated
	public List<SelectOpt> getSelectOpts(String textProperty,
			String valueProperty, String parentProperty, T parentObj,
			int deepness,
			String hql, boolean hasBrother,
			final Object... values) {
		List<SelectOpt> selectOpts = new ArrayList<SelectOpt>();
		
		SelectOpt selectOpt = new SelectOpt();
		if (deepness == 0) {
			selectOpt.setText(SelectOptUtil.start
					+ ReflectionUtils.getFieldValue(parentObj, textProperty)
							.toString());
		} else {
			StringBuilder builder = new StringBuilder();
			for (; deepness > 1; deepness--) {
				builder.append(SelectOptUtil.erect);
			}
			if (hasBrother) {
				builder.append(SelectOptUtil.middle);
			} else {
				builder.append(SelectOptUtil.end);
			}
			selectOpt.setText(builder.toString()
					+ ReflectionUtils.getFieldValue(parentObj, textProperty)
							.toString());
		}
		selectOpt.setValue(ReflectionUtils.getFieldValue(parentObj,
				valueProperty));
		selectOpts.add(selectOpt);
		
		List<T> childer = getEntityDao().find(hql, values);
		if (childer == null || childer.size() == 0)
			return selectOpts;
		deepness++;
		for (int i = 0; i < childer.size(); i++) {
			T child = childer.get(i);
			if (i == (childer.size() - 1)) {
				hasBrother = false;
			} else {
				hasBrother = true;
			}		
			values[0] = ReflectionUtils
					.getFieldValue(child, parentProperty);
			selectOpts.addAll(getSelectOpts(textProperty, valueProperty,
					parentProperty, child, deepness, hql, hasBrother, values));
		}
		return selectOpts;
	}
	
	
}
